import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

///***********************************************
///文件描述：
///创建人：dgg108698
///创建时间：2020/10/23
///修改人：
///修改时间：
///修改备注：
///***********************************************

enum DggDateUnit {
  year,
  month,
  day,
  hour,
  minute,
  // second,
}

class DggDatePicker extends StatefulWidget {
  final List<DggDateUnit> unitList;
  final DateTime initialDateTime;
  final DateTime minimumDate;
  final DateTime maximumDate;
  final ValueChanged<DateTime> onDateTimeChanged;

  DggDatePicker({
    @required this.onDateTimeChanged,
    @required this.unitList,
    DateTime initialDateTime,
    DateTime maximumDate,
    DateTime minimumDate,
  })  : initialDateTime = initialDateTime ?? DateTime.now(),
        maximumDate = maximumDate != null
            ? DateTime(
                maximumDate.year,
                maximumDate.month,
                maximumDate.day,
                maximumDate.hour,
                maximumDate.minute,
              )
            : null,
        minimumDate = minimumDate != null
            ? DateTime(
                minimumDate.year,
                minimumDate.month,
                minimumDate.day,
                minimumDate.hour,
                minimumDate.minute,
              )
            : null;

  @override
  State<StatefulWidget> createState() => _DggDatePickerState();
}

class _DggDatePickerState extends State<DggDatePicker> {
  bool isYearPickerScrolling = false;
  bool isMonthPickerScrolling = false;
  bool isDayPickerScrolling = false;
  bool isHourPickerScrolling = false;
  bool isMinuteScrolling = false;

  bool get isScrolling =>
      isDayPickerScrolling ||
      isMonthPickerScrolling ||
      isYearPickerScrolling ||
      isHourPickerScrolling ||
      isMinuteScrolling;

  FixedExtentScrollController dayController;
  FixedExtentScrollController monthController;
  FixedExtentScrollController yearController;
  FixedExtentScrollController hourController;
  FixedExtentScrollController minuteController;

  int selectedYear;
  int selectedMonth;
  int selectedDay;
  int selectedHour;
  int selectedMinute;

  DateTime _lastDayInMonth(int year, int month,
          {int hour = 0, int minute = 0}) =>
      DateTime(year, month + 1, 0, hour, minute);

  DateTime get selectedDateTime {
    return DateTime(
      selectedYear,
      selectedMonth,
      selectedDay,
      selectedHour,
      selectedMinute,
    );
  }

  @override
  void initState() {
    super.initState();
    selectedDay = widget.initialDateTime.day;
    selectedMonth = widget.initialDateTime.month;
    selectedYear = widget.initialDateTime.year;
    selectedHour = widget.initialDateTime.hour;
    selectedMinute = widget.initialDateTime.minute;
    yearController = FixedExtentScrollController(initialItem: selectedYear);
    monthController =
        FixedExtentScrollController(initialItem: selectedMonth - 1);
    dayController = FixedExtentScrollController(initialItem: selectedDay - 1);
    hourController = FixedExtentScrollController(initialItem: selectedHour);
    minuteController = FixedExtentScrollController(initialItem: selectedMinute);
  }

  @override
  void dispose() {
    yearController.dispose();
    monthController.dispose();
    dayController.dispose();
    hourController.dispose();
    minuteController.dispose();
    super.dispose();
  }

  bool _isCurrentDateValid({
    int year,
    int month,
    int day,
    int hour,
    int minute,
    bool isDayOver = true,
  }) {
    final DateTime selected = DateTime(
      year ?? selectedYear,
      month ?? selectedMonth,
      day ?? selectedDay,
      hour ?? selectedHour,
      minute ?? selectedMinute,
    );

    ///是否不通过
    final bool minCheck = widget.minimumDate?.isAfter(selected) ?? false;
    final bool maxCheck = widget.maximumDate?.isBefore(selected) ?? false;
    return minCheck == false &&
        maxCheck == false &&
        (isDayOver == true ? true : selected.day == (day ?? selectedDay));
  }

  void _pickerDidStopScrolling() {
    // Call setState to update the greyed out days/months/years, as the currently
    // selected year/month may have changed.
    setState(() {});

    if (isScrolling) {
      return;
    }

    final DateTime selectedDate = selectedDateTime;

    final bool minCheck = widget.minimumDate?.isAfter(selectedDate) ?? false;
    final bool maxCheck = widget.maximumDate?.isBefore(selectedDate) ?? false;

    if (minCheck || maxCheck) {
      // We have minCheck === !maxCheck.
      final DateTime targetDate =
          minCheck ? widget.minimumDate : widget.maximumDate;
      _scrollToDate(targetDate);
    }

    //仅在选择的日期大于本月的天数
    if (selectedDate.day != selectedDay) {
      final DateTime lastDay = _lastDayInMonth(selectedYear, selectedMonth,
          hour: selectedHour, minute: selectedMinute);
      _scrollToDate(lastDay);
    }
  }

  void _scrollToDate(DateTime newDate) {
    assert(newDate != null);
    SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
      if (selectedYear != newDate.year) {
        _animateColumnControllerToItem(yearController, newDate.year);
      }

      if (selectedMonth != newDate.month) {
        _animateColumnControllerToItem(monthController, newDate.month - 1);
      }

      if (selectedDay != newDate.day) {
        _animateColumnControllerToItem(dayController, newDate.day - 1);
      }
      if (selectedHour != newDate.hour) {
        _animateColumnControllerToItem(hourController, newDate.hour);
      }
      if (selectedMinute != newDate.minute) {
        _animateColumnControllerToItem(minuteController, newDate.minute);
      }
    });
  }

  void _animateColumnControllerToItem(
      FixedExtentScrollController controller, int targetItem) {
    controller.animateToItem(
      targetItem,
      curve: Curves.easeInOut,
      duration: const Duration(milliseconds: 200),
    );
  }

  void _onDateTimeChanged() {
    if (_isCurrentDateValid()) {
      widget.onDateTimeChanged(
        DateTime(selectedYear, selectedMonth, selectedDay, selectedHour,
            selectedMinute),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final List<Widget Function()> pickerBuilders = widget.unitList.map((e) {
      switch (e) {
        case DggDateUnit.year:
          return _buildYearPicker;
        case DggDateUnit.month:
          return _buildMonthPicker;
        case DggDateUnit.day:
          return _buildDayPicker;
        case DggDateUnit.hour:
          return _buildHourPicker;
        case DggDateUnit.minute:
          return _buildMinutePicker;
      }
    }).toList();

    return Container(
      height: 250,
      child: Row(
        children: pickerBuilders.map((e) {
          return Expanded(child: e());
        }).toList(),
      ),
    );
  }

  Widget _buildYearPicker() {
    return _buildPicker(
      controller: yearController,
      onChange: (value) {
        selectedYear = value;
        _onDateTimeChanged();
      },
      scrollChange: (value) {
        isYearPickerScrolling = value;
        if (!value) _pickerDidStopScrolling();
      },
      itemBuilder: (context, year) {
        return _buildItem('$year年',
            enable: _isCurrentDateValid(year: year),
            isSelect: year == selectedYear);
      },
    );
  }

  Widget _buildMonthPicker() {
    return _buildPicker(
      controller: monthController,
      childCount: 12,
      onChange: (value) {
        selectedMonth = value + 1;
        _onDateTimeChanged();
      },
      scrollChange: (value) {
        isMonthPickerScrolling = value;
        if (!value) _pickerDidStopScrolling();
      },
      itemBuilder: (context, index) {
        final int month = index + 1;
        return _buildItem('$month月',
            enable: _isCurrentDateValid(month: month),
            isSelect: index + 1 == selectedMonth);
      },
    );
  }

  Widget _buildDayPicker() {
    return _buildPicker(
      controller: dayController,
      childCount: 31,
      onChange: (value) {
        selectedDay = value + 1;
        _onDateTimeChanged();
      },
      scrollChange: (value) {
        isDayPickerScrolling = value;
        if (!value) _pickerDidStopScrolling();
      },
      itemBuilder: (context, index) {
        final day = index + 1;
        return _buildItem('$day日',
            enable: _isCurrentDateValid(day: day, isDayOver: false),
            isSelect: index + 1 == selectedDay);
      },
    );
  }

  Widget _buildHourPicker() {
    return _buildPicker(
      controller: hourController,
      childCount: 24,
      onChange: (value) {
        selectedHour = value;
        _onDateTimeChanged();
      },
      scrollChange: (value) {
        isHourPickerScrolling = value;
        if (!value) _pickerDidStopScrolling();
      },
      itemBuilder: (context, index) => _buildItem('$index时',
          enable: _isCurrentDateValid(hour: index),
          isSelect: index == selectedHour),
    );
  }

  Widget _buildMinutePicker() {
    return _buildPicker(
      controller: minuteController,
      childCount: 60,
      onChange: (value) {
        selectedMinute = value;
        _onDateTimeChanged();
      },
      scrollChange: (value) {
        isMinuteScrolling = value;
        if (!value) _pickerDidStopScrolling();
      },
      itemBuilder: (context, index) => _buildItem('$index分',
          enable: _isCurrentDateValid(minute: index),
          isSelect: index == selectedMinute),
    );
  }

  Widget _buildItem(String name, {bool enable = true, bool isSelect}) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        name,
        style: TextStyle(
            color: isSelect ? Color(0xFFE8AF00) : Color(0xFF999999),
            fontSize:15),
      ),
    );
  }

  Widget _buildPicker({
    FixedExtentScrollController controller,
    ValueChanged<int> onChange,
    IndexedWidgetBuilder itemBuilder,
    void Function(bool scroll) scrollChange,
    int childCount,
  }) {
    return NotificationListener(
      onNotification: (ScrollNotification notification) {
        if (notification is ScrollStartNotification) {
          scrollChange?.call(true);
        } else if (notification is ScrollEndNotification) {
          scrollChange?.call(false);
        }
        return false;
      },
      child: CupertinoPicker.builder(
        selectionOverlay: CupertinoPickerDefaultSelectionOverlay(
            background: Colors.transparent),
        scrollController: controller,
        itemExtent: 40.0,
        onSelectedItemChanged: onChange,
        itemBuilder: itemBuilder,
        childCount: childCount,
        diameterRatio: 1.0,
      ),
    );
  }
}
